/* 
   Implementação da classe estatística
   Criada como classe apenas para simplificar a main
*/


#include <cstdlib>
#include <iostream>
#include <math.h>
#include "Pacote.h"
#include "Evento.h"
#include "Lista.h"
#include "Fluxo.h"
#include "Filas.h"
#include "Servidor.h"


Rodada::Rodada()
{
	this->tamanhoRodada = 0;
} 

Rodada::Rodada(unsigned id, unsigned tamanhoRodada, unsigned numeroChamadas, unsigned numFluxosTrafegoFundo, FluxoVoz **chamadas, FluxoDados **trafegoFundo)
{
	this->id = id;
	this->tamanhoRodada = tamanhoRodada;
	this->numeroChamadas = numeroChamadas;
	this->numFluxosTrafegoFundo = numFluxosTrafegoFundo;
	this->chamadas = chamadas;
	this->trafegoFundo = trafegoFundo;

	this->pacotesGerados = 0;
	this->pacotesPerdidos = 0; 
	this->pacotesConcluidos = 0;
	this->tempoTotal = 0;	
	this->mediaTempoUltimoConjunto = 0;
	this->pctsFilaUltimoConjunto = 0;

	unsigned i;		
	for(i = 0; i < numeroChamadas; i++)
	{
		chamadas[i]->zeraEstatisticas();
		chamadas[i]->setRodada(id);		
	}
} 


bool Rodada::acabouTransiente(unsigned pacotesVozNasFilas)
{	
	unsigned i = 0;

	// se alguma chamada não tiver concluido o mínimo de pacotes para completar um conjunto, retorna falso

	for (i = 0; i < numeroChamadas; i++)	
		if (chamadas[i]->getPacotesConcluidos() < TAM_CONJUNTO_TRANSIENTE) 				return false;

	// se não retornou falso, o conjunto está completo. Calcula a média do tempo gasto pelas chamadas nesse cojnunto e zera as estatísticas das chamadas 
			
	double totTempoConjunto = 0;
	double mediaTempoConjuntoAtual = 0;	
	unsigned long pacotesConcluidosConjunto = 0;	
	unsigned pctsFilaConjuntoAtual = 0;

	for (i = 0; i < numeroChamadas; i++)		
	{
		totTempoConjunto += chamadas[i]->getTempoGasto(); 
		pacotesConcluidosConjunto += chamadas[i]->getPacotesConcluidos();		
		chamadas[i]->zeraEstatisticas();
	}

	mediaTempoConjuntoAtual = totTempoConjunto / pacotesConcluidosConjunto;			
	pctsFilaConjuntoAtual = pacotesVozNasFilas;

	// se a diferença é maior que 5%, continua o transiente e inicia outro conjunto de medidas
	bool mediaTempoInstavel = fabs(mediaTempoConjuntoAtual - mediaTempoUltimoConjunto) >  0.1 * mediaTempoUltimoConjunto;
	bool mediaPacotesInstavel = fabs(pctsFilaConjuntoAtual - pctsFilaUltimoConjunto) >  0.1 * pctsFilaUltimoConjunto;

	if ( mediaTempoInstavel) 
	{
		mediaTempoUltimoConjunto = mediaTempoConjuntoAtual;
		pctsFilaUltimoConjunto = pctsFilaConjuntoAtual;		
		return false;
	}		
	else		
	// se o sistema está saturado, então aumenta a certeza do equilíbrio com a medida de pacotes nas filas
	{		
		unsigned taxaEntrada = (numeroChamadas * 80000) + (numFluxosTrafegoFundo * 100000);
		unsigned taxaSaida = LARGURA_BANDA;

		if (taxaEntrada > taxaSaida)
		{	
			if (mediaPacotesInstavel)
			{
				mediaTempoUltimoConjunto = mediaTempoConjuntoAtual;
				pctsFilaUltimoConjunto = pctsFilaConjuntoAtual;		
				return false;
			}	
			else
				return true;
		}
		else return true;			
	}
}


bool Rodada::acabou(unsigned pacotesVoz, int verbose)
{
	unsigned i;

	if (id == 0) 
		return acabouTransiente(pacotesVoz);

	for (i = 0; i < numeroChamadas; i++)
		if (chamadas[i]->getPacotesConcluidos() < tamanhoRodada)
			return false;

	this->encerra(verbose);
	printf ("Pacotes nas filas de voz %u\n\n", pacotesVoz);		
	return true;	
	
}	


void Rodada::encerra(int verbose)
{
	unsigned i;

	estatisticas(verbose);
	estatisticasFluxosDados(verbose);

	for (i = 0; i < numeroChamadas; i++)
		chamadas[i]->zeraEstatisticas();
	for (i = 0; i < numFluxosTrafegoFundo; i++)
		trafegoFundo[i]->zeraEstatisticas();	

}


void Rodada::estatisticas (int verbose) {

	unsigned i;
	
	for (i = 0; i < numeroChamadas; i++)  {

		unsigned gerados = chamadas[i]->getPacotesGerados();        
		unsigned perdidos = chamadas[i]->getPacotesPerdidos();
		unsigned concluidos = chamadas[i]->getPacotesConcluidos();
		double somaTempos = chamadas[i]->getTempoGasto();
	
		double perdaMedia = (double) ((double)perdidos / (double)gerados) * 100.0;
		double tempoMedio = (double) (somaTempos / (double)concluidos);

		pacotesGerados += gerados;
		pacotesPerdidos += perdidos;
		pacotesConcluidos += concluidos;
		tempoTotal += somaTempos;

		if (verbose >= 1) 
			printf("Chamada %d - Pacotes gerados: %u | Pacotes perdidos: %u | Perda Media %.02lf%% | Tempo Medio Gasto: %lf\n", i, gerados, perdidos, perdaMedia, tempoMedio);
	}
		
	if (verbose >= 0) {
		printf ("\nPerda media das chamadas na rodada %u: %.02lf%%\n", id, (double)((double) pacotesPerdidos /  (double)pacotesGerados) * 100.0);
		printf ("Tempo medio gasto pelos pacotes de voz na rodada %u: %lf\n\n", id, (double) ( tempoTotal / (double)pacotesConcluidos ) );
	}

}


void Rodada::estatisticasFluxosDados (int verbose) {
	
	long numeroChegadas = 0;	
	double tempoTotalEntreChegadas = 0;   
	unsigned long totalPerdidos = 0;
	unsigned i;
	
	if (numFluxosTrafegoFundo != 0 )
	{
		for (i = 0; i < numFluxosTrafegoFundo; i++)  {       

			unsigned gerados = trafegoFundo[i]->getPacotesGerados();        
			unsigned perdidos = trafegoFundo[i]->getPacotesPerdidos();
			double tempoEntreChegadas = trafegoFundo[i]->getTempoEntreChegadas();
	
			double perdaMedia = (double) ( (double) perdidos / (double) gerados ) * 100.0;
	
			numeroChegadas += gerados;
			tempoTotalEntreChegadas += tempoEntreChegadas;
			totalPerdidos += perdidos;

			if (verbose >= 4)
				printf("Conexao de dados %d - Pacotes gerados: %u | Pacotes perdidos: %u | Perda Media %.02lf%% | Media de tempo entre chegadas de dados: %lf \n", i, gerados, perdidos, perdaMedia, tempoEntreChegadas / gerados); 
		}          

		if (verbose >= 1)
			printf ("Total de pacotes de dados: %lu - Media de tempo entre chegadas: %lf - Perda Media %.02lf%% \n", numeroChegadas, tempoTotalEntreChegadas / numeroChegadas, (double) ( (double) totalPerdidos / (double) numeroChegadas ) * 100.0 );       	
	}
}


void IC::calcula (Rodada *rodadas, unsigned numRodadas)
{
	double somaAmostras = 0;
	double mediaAmostras = 0;
	double somaVariancias = 0;
	double varianciaAmostras = 0;
	unsigned i;	
	
	for (i = 1; i <= numRodadas; i++) 		
	{
		double perdaMedia = 100.0 * (double) ((double)rodadas[i].pacotesPerdidos / (double)rodadas[i].pacotesGerados); 
		somaAmostras += perdaMedia;
	}
	
	mediaAmostras = somaAmostras / numRodadas;
	
	for (i = 1; i <= numRodadas; i++) {
		
		double amostra = 100.0 * (double) ((double)rodadas[i].pacotesPerdidos / (double)rodadas[i].pacotesGerados); 
		double sigma =  amostra - mediaAmostras;
		double variancia = sigma * sigma;
		somaVariancias += variancia;
	}				
	
	varianciaAmostras = somaVariancias / (numRodadas - 1);

	centro = mediaAmostras;
	tamanho = 2 * sqrt(varianciaAmostras) * ASSINTOTICO_90 / sqrt(numRodadas);
	L = centro - tamanho / 2;
	U = centro + tamanho / 2;
}


void IC::exibe()
{
	printf ("Centro do intervalo: %.04lf%%\n", centro);
	printf ("Tamanho do intervalo de confianca de 90%%: %.04lf\n", tamanho);
}

